/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.architexa.org.eclipse.gef.tools;

import com.architexa.org.eclipse.draw2d.IFigure;
import com.architexa.org.eclipse.draw2d.geometry.Dimension;
import com.architexa.org.eclipse.draw2d.geometry.Point;
import com.architexa.org.eclipse.gef.DragTracker;
import com.architexa.org.eclipse.gef.EditDomain;
import com.architexa.org.eclipse.gef.EditPartViewer;
import com.architexa.org.eclipse.gef.LayerConstants;
import com.architexa.org.eclipse.gef.MouseWheelHandler;
import com.architexa.org.eclipse.gef.RequestConstants;
import com.architexa.org.eclipse.gef.Tool;
import com.architexa.org.eclipse.gef.commands.Command;
import com.architexa.org.eclipse.gef.commands.CommandStackListener;
import com.architexa.org.eclipse.gef.editparts.LayerManager;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Scrollable;



/**
 * The base implementation for {@link Tool}s.  The base implementation provides a
 * framework for a <EM>state machine</EM> which processes mouse and keyboard input.  The
 * state machine consists of a series of states identified by <code>int</code>s. Each
 * mouse or keyboard event results in a transition, sometimes to the same state in which
 * the input was received.  The interesting transitions have corresponding actions
 * assigned to them, such as {@link #handleDragStarted()}.
 * <P>
 * The base implementation performs <EM>no</em> state transitions by default, but does
 * route events to different method handlers based on state.  It is up to subclasses to
 * set the appropriate states.
 * <P>
 * There are two broad "categories" of methods on AbstractTool.  There are the methods
 * defined on the {@link Tool} interface which handle the job of receiving raw user input.
 *  For example, {@link #mouseDrag(MouseEvent, EditPartViewer)}.  Then, there are the
 * methods which correspond to higher-level interpretation of these events, such as {@link
 * #handleDragInProgress()}, which is called from <code>mouseMove(...)</code>, but
 * <em>only</em> when the drag threshold has been passed. These methods are generally
 * more subclass-friendly. Subclasses should <em>not</em> override the methods which
 * receive raw input.
 */
public abstract class AbstractTool
	extends com.architexa.org.eclipse.gef.util.FlagSupport
	implements Tool, RequestConstants
{

/**
 * The property to be used in {@link #setProperties(Map)} for 
 * {@link #setUnloadWhenFinished(boolean)}
 */
public static final Object PROPERTY_UNLOAD_WHEN_FINISHED = "unloadWhenFinished"; //$NON-NLS-1$

private static final int DRAG_THRESHOLD = 5;
private static final int FLAG_ACTIVE = 8;
private static final int FLAG_HOVER = 2;
private static final int FLAG_PAST_THRESHOLD = 1;
private static final int FLAG_UNLOAD = 4;
{
	setFlag(FLAG_UNLOAD, true);
}

/**
 * The highest-bit flag being used.
 */
protected static final int MAX_FLAG = 8;

/**
 * The maximum state flag defined by this class
 */
protected static final int MAX_STATE = 32;

/**
 * constant used for mouse button 1.
 * @deprecated Use {@link SWT#BUTTON1} instead.
 */
protected static final int MOUSE_BUTTON1 = SWT.BUTTON1;
/**
 * constant used for mouse button 2.
 * @deprecated Use {@link SWT#BUTTON2} instead.
 */
protected static final int MOUSE_BUTTON2 = SWT.BUTTON2;
/**
 * constant used for mouse button 3.
 * @deprecated Use {@link SWT#BUTTON3} instead.
 */
protected static final int MOUSE_BUTTON3 = SWT.BUTTON3;
/**
 * constant used to indicate any of the mouse buttons.
 * @deprecated Use {@link SWT#BUTTON_MASK} instead.
 */
protected static final int MOUSE_BUTTON_ANY = SWT.BUTTON_MASK;

/**
 * The state indicating that the keyboard is being used to perform a drag that is normally
 * done using the mouse.
 */
protected static final int STATE_ACCESSIBLE_DRAG = 16;

/**
 * The state indicating that a keyboard drag is in progress.  The threshold for keyboard
 * drags is non-existent, so this state would be entered very quickly.
 */
protected static final int STATE_ACCESSIBLE_DRAG_IN_PROGRESS = 32;

/**
 * The state indicating that one or more buttons are pressed, but the user has not moved
 * past the drag threshold.  Many tools will do nothing during this state but wait
 * until {@link #STATE_DRAG_IN_PROGRESS} is entered.
 */
protected static final int STATE_DRAG = 2;

/**
 * The state indicating that the drag detection theshold has been passed, and a drag is in
 * progress.
 */
protected static final int STATE_DRAG_IN_PROGRESS = 4;

/**
 * The first state that a tool is in.  The tool will generally be in this state
 * immediately following {@link #activate()}.
 */
protected static final int STATE_INITIAL  = 1;

/**
 * The state indicating that an input event has invalidated the interaction.  For example,
 * during a mouse drag, pressing additional mouse button might invalidate the drag.
 */
protected static final int STATE_INVALID = 8;

/**
 * The final state for a tool to be in.  Once a tool reaches this state, it will not
 * change states until it is activated() again.
 */
protected static final int STATE_TERMINAL = 1 << 30;

/**
 * Key modifier for ignoring snap while dragging.  It's CTRL on Mac, and ALT on all
 * other platforms.
 */
static final int MODIFIER_IGNORE_SNAP;

private long accessibleBegin;

private int accessibleStep;
private Command command;

private CommandStackListener commandStackListener = new CommandStackListener() {
	public void commandStackChanged(EventObject event) {
		handleCommandStackChanged();
	}
};
private Input current;
private EditPartViewer currentViewer;
private Cursor defaultCursor, disabledCursor;
private EditDomain domain;
private List operationSet;
private int startX, startY, state;

static {
	if (SWT.getPlatform().equals("carbon"))//$NON-NLS-1$
		MODIFIER_IGNORE_SNAP = SWT.CTRL;
	else
		MODIFIER_IGNORE_SNAP = SWT.ALT;
}

boolean acceptAbort(KeyEvent e) {
	return e.character == SWT.ESC;
}

boolean acceptArrowKey(KeyEvent e) {
	int key = e.keyCode;
	if (!(isInState(STATE_INITIAL
	  | STATE_ACCESSIBLE_DRAG
	  | STATE_ACCESSIBLE_DRAG_IN_PROGRESS)))
		return false;
	return (key == SWT.ARROW_UP)
		|| (key == SWT.ARROW_RIGHT)
		|| (key == SWT.ARROW_DOWN)
		|| (key == SWT.ARROW_LEFT);
}

boolean acceptDragCommit(KeyEvent e) {
	return isInState(STATE_ACCESSIBLE_DRAG_IN_PROGRESS)
		&& e.character == 13;
}

int accGetStep() {
	return accessibleStep;
}

void accStepIncrement() {
	if (accessibleBegin == -1) {
		accessibleBegin = new Date().getTime();
		accessibleStep = 1;
	} else {
		accessibleStep = 4;
		long elapsed = new Date().getTime() - accessibleBegin;
		if (elapsed > 1000)
			accessibleStep = Math.min(16, (int) (elapsed / 150));
	}
}

void accStepReset() {
	accessibleBegin = -1;
}

/**
 * Activates the tool.  Any initialization should be performed here.
 * This method is called when a tool is selected.
 * 
 * @see #deactivate()
 */
public void activate() {
	resetFlags();
	accessibleBegin = -1;
	getCurrentInput().verifyMouseButtons = true;
	setState(STATE_INITIAL);
	setFlag(FLAG_ACTIVE, true);
	getDomain().getCommandStack().addCommandStackListener(commandStackListener);
}

/**
 * Convenience method to add the given figure to the feedback layer.
 * @param figure the feedback being added
 */
protected void addFeedback(IFigure figure) {
	LayerManager lm = (LayerManager)getCurrentViewer().
		getEditPartRegistry().get(LayerManager.ID);
	if (lm == null)
		return;
	lm.getLayer(LayerConstants.FEEDBACK_LAYER).add(figure);
}

/**
 * This method is invoked from {@link #setProperties(Map)}.  Sub-classes can override to
 * add support for more properties.  This method should fail silently in case of any
 * error.
 * <p>
 * AbstractTool uses introspection to match any keys with properties.  For instance, the
 * key "defaultCursor" would lead to the invocation of {@link #setDefaultCursor(Cursor)}
 * with the provided value.
 * @param key the key; may be <code>null</code>
 * @param value the new value
 * @since 3.1
 * @see #setProperties(Map)
 */
protected void applyProperty(Object key, Object value) {
	if (PROPERTY_UNLOAD_WHEN_FINISHED.equals(key)) {
		if (value instanceof Boolean)
			setUnloadWhenFinished(((Boolean)value).booleanValue());
		return;
	}
	
	if (!(key instanceof String))
		return;
	
	try {
		PropertyDescriptor[] descriptors = Introspector
				.getBeanInfo(getClass(), Introspector.IGNORE_ALL_BEANINFO)
				.getPropertyDescriptors();
		PropertyDescriptor property = null;
		for (int i = 0; i < descriptors.length; i++) {
			if (descriptors[i].getName().equals(key)) {
				property = descriptors[i];
				break;
			}
		}
		if (property != null) {
			Method setter = property.getWriteMethod();
//			setter.setAccessible(true);
			setter.invoke(this, new Object[] {value});
		}
	} catch (IntrospectionException ie) {
	} catch (IllegalAccessException iae) {
	} catch (InvocationTargetException ite) {
	} catch (SecurityException se) { }
}

/**
 * Returns the appropriate cursor for the tools current state.  If the tool is in its
 * terminal state, <code>null</code> is returned.  Otherwise, either the default or
 * disabled cursor is returned, based on the existence of a current command, and whether
 * that current command is executable.
 * <P>
 * Subclasses may override or extend this method to calculate the appropriate cursor based
 * on other conditions.
 * @see #getDefaultCursor()
 * @see #getDisabledCursor()
 * @see #getCurrentCommand()
 * @return <code>null</code> or a cursor to be displayed.
 */
protected Cursor calculateCursor() {
	if (isInState(STATE_TERMINAL))
		return null;
	Command command = getCurrentCommand();
	if (command == null || !command.canExecute())
		return getDisabledCursor();
	return getDefaultCursor();
}

/**
 * Added for compatibility.  {@link DragTracker#commitDrag()} was added for accessibility
 * reasons.  Since all tool implementations must inherit from this base class, then
 * implementing this method here avoids breaking subclasses that implemented the {@link
 * DragTracker} interface.
 */
public void commitDrag() { }

/**
 * Returns a new List of editparts that this tool is operating on.  This method is called
 * once during {@link #getOperationSet()}, and its result is cached.
 * <P>
 * By default, the operations set is the current viewer's entire selection.  Subclasses
 * may override this method to filter or alter the operation set as necessary.
 * @return a list of editparts being operated on
 */
protected List createOperationSet() {
	return new ArrayList(getCurrentViewer().getSelectedEditParts());
}

/**
 * Deactivates the tool. This method is called whenever the user switches to another tool.
 * Use this method to do some clean-up when the tool is switched. The abstract tool allows
 * cursors for  viewers to be changed. When the tool is deactivated it must  revert to
 * normal the cursor of the last tool it changed.
 * @see #activate()
 */
public void deactivate() {
	setFlag(FLAG_ACTIVE, false);
	setViewer(null);
	setCurrentCommand(null);
	setState(STATE_TERMINAL);
	operationSet = null;
	current = null;
	getDomain().getCommandStack().removeCommandStackListener(commandStackListener);
}

/**
 * Prints a string in the GEF Debug console if the Tools debug option is selected.
 * @param message a message for the debug trace tool
 * @deprecated
 */
protected void debug(String message) {
}

/**
 * Executes the given command on the command stack.
 * @since 3.1
 * @param command the command to execute
 */
protected void executeCommand(Command command) {
	getDomain().getCommandStack().removeCommandStackListener(commandStackListener);
	try {
		getDomain().getCommandStack()
			.execute(command);
	} finally {
		getDomain().getCommandStack().addCommandStackListener(commandStackListener);
	}
}

/**
 * Execute the currently active command.
 */
protected void executeCurrentCommand() {
	Command curCommand = getCurrentCommand();
	if (curCommand != null && curCommand.canExecute())
		executeCommand(curCommand);
	setCurrentCommand(null);
}

/**
 * Called when a viewer that the editor controls gains focus.
 * 
 * @param event The SWT focus event 
 * @param viewer The viewer that the focus event is over.
 */
public void focusGained(FocusEvent event, EditPartViewer viewer) {
	setViewer(viewer);
	handleFocusGained();
}

/**
 * Called when a viewer that the editor controls loses focus.
 * 
 * @param event The SWT focus event 
 * @param viewer The viewer that the focus event is over.
 */
public void focusLost(FocusEvent event, EditPartViewer viewer) {
	setViewer(viewer);
	handleFocusLost();
}

/**
 * Returns a new, updated command based on the tool's current properties.  The default
 * implementation returns an unexecutable command.  Some tools do not work commands and
 * the model, but simply change the viewer's state in some way.
 * @return a newly obtained command
 */
protected Command getCommand() {
	return com.architexa.org.eclipse.gef.commands.UnexecutableCommand.INSTANCE;
}

/**
 * Returns the identifier of the command that is being sought.  This name is also the 
 * named that will be logged in the debug view.
 * @return the identifier for the command
 */
protected abstract String getCommandName();

/**
 * Returns the currently cached command.
 * @return the current command
 * @see #setCurrentCommand(Command)
 */
protected Command getCurrentCommand() {
	return command;
}

/**
 * Returns the input object encapsulating the current mouse and keyboard state.
 * @return the current input
 */
protected Input getCurrentInput() {
	if (current == null)
		current = new Input();
	return current;
}

/**
 * Return the viewer that the tool is currently receiving input from, or
 * <code>null</code>.  The last viewer to dispatch an event is defined as the current
 * viewer.  Current viewer is automatically updated as events are received, and is set to
 * <code>null</code> on <code>deactivate()</code>.
 * @return the current viewer
 */
protected EditPartViewer getCurrentViewer() {
	return currentViewer;
}

/**
 * Returns the debug name for this tool.
 * @return the debug name
 */
protected String getDebugName() {
	return getClass().getName();
}

/**
 * Returns a String representation of the given state for debug purposes.
 * @param state the state
 * @return the string for the given state
 */
protected String getDebugNameForState(int state) {
	switch (state) {
		case STATE_INITIAL:
			return "Initial State";//$NON-NLS-1$
		case STATE_DRAG:
			return "Drag State";//$NON-NLS-1$
		case STATE_DRAG_IN_PROGRESS:
			return "Drag In Progress State";//$NON-NLS-1$
		case STATE_INVALID:
			return "Invalid State"; //$NON-NLS-1$
		case STATE_TERMINAL:
			return "Terminal State"; //$NON-NLS-1$
		case STATE_ACCESSIBLE_DRAG:
			return "Accessible Drag"; //$NON-NLS-1$
		case STATE_ACCESSIBLE_DRAG_IN_PROGRESS:
			return "Accessible Drag In Progress"; //$NON-NLS-1$
	}
	return "Unknown state:";//$NON-NLS-1$
}

/**
 * Returns the cursor used under normal conditions.
 * @see #setDefaultCursor(Cursor)
 * @return the default cursor
 */
protected Cursor getDefaultCursor() {
	return defaultCursor;
}

/**
 * Returns the cursor used under abnormal conditions.
 * @see #calculateCursor()
 * @see #setDisabledCursor(Cursor)
 * @return the disabled cursor
 */
protected Cursor getDisabledCursor() {
	if (disabledCursor != null)
		return disabledCursor;
	return getDefaultCursor();
}

/**
 * Returns the EditDomain.  A tool is told its EditDomain when it becomes active.  A tool
 * may need to know its edit domain prior to receiving any events from any of that
 * domain's viewers.
 * @return the editdomain
 */
protected EditDomain getDomain() {
	return domain;
}

/**
 * Return the number of pixels that the mouse has been moved since that drag was started.
 * The drag start is determined by where the mouse button was first pressed.
 * @see #getStartLocation()
 * @return the drag delta
 */
protected Dimension getDragMoveDelta() {
	return getLocation().getDifference(getStartLocation());
}

/**
 * Returns the current x, y position of the mouse cursor.
 * @return the mouse location
 */
protected Point getLocation() {
	return new Point(getCurrentInput().getMouseLocation());
}

/**
 * Lazily creates and returns the list of editparts on which the tool operates.  The list
 * is initially <code>null</code>, in which case {@link #createOperationSet()} is called,
 * and its results cached until the tool is deactivated.
 * @return the operation set.
 */
protected List getOperationSet() {
	if (operationSet == null)
		operationSet = createOperationSet();
	return operationSet;
}

/**
 * Returns the starting mouse location for the current tool operation.  This is typically
 * the mouse location where the user first pressed a mouse button.  This is important for
 * tools that interpret mouse drags.
 * @return the start location
 */
protected Point getStartLocation() {
	return new Point(startX, startY);
}

/**
 * Returns the tool's current state.
 * @return the current state
 */
protected int getState() {
	return state;
}

/**
 * Called when the mouse button has been pressed. By default, nothing happens
 * and <code>false</code> is returned. Subclasses may override this method to interpret
 * the meaning of a mouse down. Returning <code>true</code> indicates that the button down
 * was handled in some way.
 * @param button which button went down
 * @return <code>true</code> if the buttonDown was handled
 */
protected boolean handleButtonDown(int button) {
	return false;
}

/**
 * Called when the mouse button has been released.  By default, nothing happens and
 * <code>false</code> is returned. Subclasses may override this method to interpret the
 * mouse up.  Returning <code>true</code> indicates that the mouse up was handled in some
 * way.
 * @see #mouseUp(MouseEvent, EditPartViewer)
 * @param button the button being released
 * @return <code>true</code> if the button up was handled
 */
protected boolean handleButtonUp(int button) {
	return false;
}

/**
 * Called when the command stack has changed, for instance, when a delete or undo command
 * has been executed. By default, state is set to <code>STATE_INVALID</code> and 
 * handleInvalidInput is called.  Subclasses may override this method to change what 
 * happens when the command stack changes. Returning <code>true</code> indicates that the
 * change was handled in some way.
 * @return <code>true</code> if the change was handled in some way
 */
protected boolean handleCommandStackChanged() {
	if (!isInState(STATE_INITIAL)) {
		setState(STATE_INVALID);
		handleInvalidInput();
		return true;
	}
	return false;
}

/**
 * Called when a mouse double-click occurs.  By default, nothing happens and
 * <code>false</code> is returned.  Subclasses may override this method to interpret
 * double-clicks.  Returning <code>true</code> indicates that the event was handled in
 * some way.
 * @param button which button was double-clicked
 * @return <code>true</code> if the event was handled
 * @see #mouseDoubleClick(MouseEvent, EditPartViewer)
 */
protected boolean handleDoubleClick(int button) {
	return false;
}

/**
 * Called whenever the mouse is being dragged.  This method continues to be called
 * even once {@link #handleDragInProgress()} starts getting called. By default, nothing
 * happens, and <code>false</code> is returned.  Subclasses may override this method to
 * interpret a drag.  Returning <code>true</code> indicates that the drag was handled in
 * some way.
 * @return <code>true</code> if the drag is handled 
 * @see #mouseDrag(MouseEvent, EditPartViewer)
 */
protected boolean handleDrag() {
	return false;
}

/**
 * Called whenever a mouse is being dragged and the drag threshold has been exceeded.
 * Prior to the drag threshold being exceeded, only {@link #handleDrag()} is called.  This
 * method gets called repeatedly for every mouse move during the drag. By default, nothing
 * happens and <code>false</code> is returned.  Subclasses may override this method to
 * interpret the drag.  Returning <code>true</code> indicates that the drag was handled.
 * @see #movedPastThreshold()
 * @see #mouseDrag(MouseEvent, EditPartViewer)
 * @return <code>true</code> if the drag was handled
 */
protected boolean handleDragInProgress() {
	return false;
}

/**
 * Called only one time during a drag when the drag threshold has been exceeded.  By
 * default, nothing happens and <code>false</code> is returned.  Subclasses may override
 * to interpret the drag starting.  Returning <code>true</code> indicates that the event
 * was handled.
 * @see #movedPastThreshold()
 * @see #mouseDrag(MouseEvent, EditPartViewer)
 * @return true if the drag starting was handled
 */
protected boolean handleDragStarted() {
	return false;
}

/**
 * Called when the current tool operation is to be completed.  In other words, the "state
 * machine" and has accepted the sequence of input (i.e. the mouse gesture).  By default,
 * the tool will either reactivate itself, or ask the edit domain to load the default
 * tool.
 * <P>
 * Subclasses should extend this method to first do whatever it is that the tool does, and
 * then call <code>super</code>.
 * @see #unloadWhenFinished()
 */
protected void handleFinished() {
	if (unloadWhenFinished())
		getDomain().loadDefaultTool();
	else
		reactivate();
}

/**
 * Handles high-level processing of a focus gained event.  By default, nothing happens and
 * <code>false</code> is returned.  Subclasses may override this method to interpret the
 * focus gained event.  Return <code>true</code> to indicate that the event was processed.
 * @see #focusGained(FocusEvent, EditPartViewer)
 * @return <code>true</code> if the event was handled
 */
protected boolean handleFocusGained() {
	return false;
}

/**
 * Handles high-level processing of a focus lost event.  By default, nothing happens and
 * <code>false</code> is returned.  Subclasses may override this method to interpret the
 * focus lost event.  Return <code>true</code> to indicate that the event was processed.
 * @see #focusLost(FocusEvent, EditPartViewer)
 * @return <code>true</code> if the event was handled
 */
protected boolean handleFocusLost() {
	return false;
}

/**
 * Handles high-level processing of a mouse hover event.  By default, nothing happens and
 * <code>false</code> is returned.  Subclasses may override this method to interpret the
 * hover.  Return <code>true</code> to indicate that the hover was handled.
 * @see #mouseHover(MouseEvent, EditPartViewer)
 * @return <code>true</code> if the hover was handled
 */
protected boolean handleHover() {
	return false;
}

/**
 * Called when invalid input is encountered. The state does not change, so the
 * caller must set the state to {@link AbstractTool#STATE_INVALID}.
 * @return <code>true</code>
 */
protected boolean handleInvalidInput() {
	return false;
}

/**
 * Handles high-level processing of a key down event.  By default, the KeyEvent is
 * checked to see if it is the ESCAPE key.  If so, the domain's default tool is reloaded,
 * and <code>true</code> is returned. Subclasses may extend this method to interpret
 * additional key down events. Returns <code>true</code> if the given key down was
 * handled.
 * @see #keyDown(KeyEvent, EditPartViewer)
 * @param e the key event
 * @return <code>true</code> if the key down was handled.
 */
protected boolean handleKeyDown(KeyEvent e) {
	if (acceptAbort(e)) {
		getDomain().loadDefaultTool();
		return true;
	}
	return false;
}

/**
 * Override to process a traverse event.  If the event's {@link KeyEvent#doit doit} field
 * is set to <code>false</code>, the traversal will be prevented from occurring. 
 * Otherwise, a traverse will occur.
 * @param event the SWT traverse event
 * @since 3.1
 */
protected void handleKeyTraversed(TraverseEvent event) {
}

/**
 * Handles high-level processing of a key up event.  By default, does nothing and returns
 * <code>false</code>.  Subclasses may extend this method to process key up events. 
 * Returns <code>true</code> if the key up was processed in some way.
 * @see #keyUp(KeyEvent, EditPartViewer)
 * @param e the key event
 * @return <code>true</code> if the event was handled
 */
protected boolean handleKeyUp(KeyEvent e) {
	return false;
}

/**
 * Handles high-level processing of a mouse move.  By default, does nothing and returns
 * <code>false</code>.  Subclasses may extend this method to process mouse moves. 
 * Returns <code>true</code> if the mouse move was processed.
 * @see #mouseMove(MouseEvent, EditPartViewer)
 * @return <code>true</code> if the mouse move was handled
 */
protected boolean handleMove() {
	return false;
}

/**
 * Handles when a native drag has ended.  By default, does nothing and returns
 * <code>false</code>. Subclasses may extend this method to process native drags ending.
 * @param event the drag event
 * @return <code>true</code> if the native drag finished was handled
 */
protected boolean handleNativeDragFinished(DragSourceEvent event) {
	return false;
}

/**
 * Handles when a native drag has started.  By default, does nothing and returns
 * <code>false</code>. Subclasses may extend this method to process native drag starts.
 * <P>
 * When a native drag starts, all subsequent mouse events will not be received, including
 * the mouseUp event.  The only event that will be received is the drag finished event.
 * @param event the drag event
 * @return <code>true</code> if the native drag start was handled
 */
protected boolean handleNativeDragStarted(DragSourceEvent event) {
	return false;
}

/**
 * Called when the mouse enters an EditPartViewer. By default, does nothing and returns
 * <code>false</code>. Subclasses may extend this method to process the viewer enter.
 * Returns <code>true</code> to indicate if the viewer entered was process in some way.
 * @return <code>true</code> if the viewer entered was handled
 */
protected boolean handleViewerEntered() {
	return false;
}

/**
 * Called when the mouse exits an EditPartViewer. By default, does nothing and returns
 * <code>false</code>. Subclasses may extend this method to process viewer exits. Returns
 * <code>true</code> to indicate if the viewer exited was process in some way.
 * @return <code>true</code> if the viewer exited was handled
 */
protected boolean handleViewerExited() {
	return false;
}

/**
 * Returns <code>true</code> if the tool is active.
 * @return <code>true</code> if active
 */
protected boolean isActive() {
	return getFlag(FLAG_ACTIVE);
}

boolean isCurrentViewerMirrored() {
	return (getCurrentViewer().getControl().getStyle() & SWT.MIRRORED) != 0;
}

/**
 * Returns <code>true</code> if the tool is hovering.
 * @return <code>true</code> if hovering
 */
protected boolean isHoverActive() {
	return getFlag(FLAG_HOVER);
}

boolean isInDragInProgress() {
	return isInState(STATE_DRAG_IN_PROGRESS | STATE_ACCESSIBLE_DRAG_IN_PROGRESS);
}

/*
 * Returns <code>true</code> if the current {@link Input} is
 * synchronized with the current MouseEvent.
 */
private boolean isInputSynched(MouseEvent event) {
	Input input = getCurrentInput();
	return input.isMouseButtonDown(1) == ((event.stateMask & SWT.BUTTON1) != 0)
			&& input.isMouseButtonDown(2) == ((event.stateMask & SWT.BUTTON2) != 0)
			&& input.isMouseButtonDown(3) == ((event.stateMask & SWT.BUTTON3) != 0)
			&& input.isMouseButtonDown(4) == ((event.stateMask & SWT.BUTTON4) != 0)
			&& input.isMouseButtonDown(5) == ((event.stateMask & SWT.BUTTON5) != 0);
}

/**
 * Returns <code>true</code> if the tool is in the given state.
 * @param state the state being queried
 * @return <code>true</code> if the tool is in the given state
 */
protected boolean isInState(int state) {
	return ((getState() & state) != 0);
}

/**
 * Default implementation always returns <code>true</code>.  Sub-classes may override.
 * @param viewer the viewer where the event occured
 * @return <code>true</code> if this tool is interested in events occuring in the given
 * viewer; <code>false</code> otherwise
 * @since 3.1
 */
protected boolean isViewerImportant(EditPartViewer viewer) {
	return true;
}

/**
 * Receives a KeyDown event for the given viewer.  Subclasses wanting to handle this
 * event should override {@link #handleKeyDown(KeyEvent)}.
 * @param evt the key event
 * @param viewer the originating viewer
 */
public void keyDown(KeyEvent evt, EditPartViewer viewer) {
	if (!isViewerImportant(viewer))
		return;
	setViewer(viewer);
	getCurrentInput().setInput(evt);
	handleKeyDown(evt);
}

/**
 * Receives a traversal event for the given viewer.  Subclasses wanting to handle this
 * event should override {@link #handleKeyTraversed(TraverseEvent)}.
 * @param event the traverse event
 * @param viewer the originating viewer
 */
public void keyTraversed(TraverseEvent event, EditPartViewer viewer) {
	if (!isViewerImportant(viewer))
		return;
	setViewer(viewer);
	getCurrentInput().setInput(event);
	handleKeyTraversed(event);
}

/**
 * Receives a KeyUp event for the given viewer.  Subclasses wanting to handle this event
 * should override {@link #handleKeyUp(KeyEvent)}.
 * @param evt the key event
 * @param viewer the originating viewer
 */
public void keyUp(KeyEvent evt, EditPartViewer viewer) {
	if (!isViewerImportant(viewer))
		return;
	setViewer(viewer);
	getCurrentInput().setInput(evt);
	handleKeyUp(evt);
}

/**
 * Handles mouse double click events within a viewer.  Subclasses wanting to handle this
 * event should override {@link #handleDoubleClick(int)}.
 * @param me the mouse event
 * @param viewer the originating viewer
 */
public void mouseDoubleClick(MouseEvent me, EditPartViewer viewer) {
	if (me.button > 5 || !isViewerImportant(viewer))
		return;
	setViewer(viewer);
	getCurrentInput().setInput(me);

	handleDoubleClick(me.button);
}

/**
 * Handles mouse down events within a viewer.  Subclasses wanting to handle this event
 * should override {@link #handleButtonDown(int)}.
 * @param me the mouse event
 * @param viewer the originating viewer
 */
public void mouseDown(MouseEvent me, EditPartViewer viewer) {
	if (!isViewerImportant(viewer))
		return;
	setViewer(viewer);

	getCurrentInput().setInput(me);
	getCurrentInput().setMouseButton(me.button, true);

	startX = me.x;
	startY = me.y;

	handleButtonDown(me.button);
}

/**
 * Handles mouse drag events within a viewer.  Subclasses wanting to handle this event
 * should override {@link #handleDrag()} and/or {@link #handleDragInProgress()}.
 * @param me the mouse event
 * @param viewer the originating viewer
 */
public void mouseDrag(MouseEvent me, EditPartViewer viewer) {
	if (!isViewerImportant(viewer))
		return;
	setViewer(viewer);
	boolean wasDragging = movedPastThreshold();
	getCurrentInput().setInput(me);
	handleDrag();
	if (movedPastThreshold()) {
		if (!wasDragging)
			handleDragStarted();
		handleDragInProgress();
	}
}

/**
 * Handles mouse hover event. within a viewer.  Subclasses wanting to handle this event
 * should override {@link #handleHover()}.
 * @param me the mouse event
 * @param viewer the originating viewer
 * 
 */
public void mouseHover(MouseEvent me, EditPartViewer viewer) {
	if (!isViewerImportant(viewer))
		return;
	setViewer(viewer);
	getCurrentInput().setInput(me);
	handleHover();
}

/**
 * Handles mouse moves (if the mouse button is up) within a viewer. Subclasses wanting to
 * handle this event should override  {@link #handleMove()}.
 * @param me the mouse event
 * @param viewer the originating viewer
 */
public void mouseMove(MouseEvent me, EditPartViewer viewer) {
	if (!isViewerImportant(viewer))
		return;
	setViewer(viewer);
	if (!isInputSynched(me)) {
		boolean b1 = getCurrentInput().isMouseButtonDown(1);
		boolean b2 = getCurrentInput().isMouseButtonDown(2);
		boolean b3 = getCurrentInput().isMouseButtonDown(3);
		boolean b4 = getCurrentInput().isMouseButtonDown(4);
		boolean b5 = getCurrentInput().isMouseButtonDown(5);
		getCurrentInput().verifyMouseButtons = true;
		getCurrentInput().setInput(me);
		if (b1) handleButtonUp(1);
		if (b2) handleButtonUp(2);
		if (b3) handleButtonUp(3);
		if (b4) handleButtonUp(4);
		if (b5) handleButtonUp(5);
		if (getDomain().getActiveTool() != this)
			return;
		/* 
		 * processing one of the buttonUps may have caused the tool to reactivate itself,
		 * which causes the viewer to get nulled-out.  If we are going to call another
		 * handleXxx method below, we must set the viewer again to be paranoid.
		 */
		setViewer(viewer);
	} else
		getCurrentInput().setInput(me);
	if (isInState(STATE_ACCESSIBLE_DRAG_IN_PROGRESS))
		handleDragInProgress();
	else
		handleMove();
}

/**
 * Handles mouse up within a viewer.  Subclasses wanting to handle this event should
 * override {@link #handleButtonUp(int)}.
 * @param me the mouse event
 * @param viewer the originating viewer
 */
public void mouseUp(MouseEvent me, EditPartViewer viewer) {
	if (!isViewerImportant(viewer))
		return;
	setViewer(viewer);
	getCurrentInput().setInput(me);
	getCurrentInput().setMouseButton(me.button, false);
	handleButtonUp(me.button);
}

/**
 * Handles mouse-wheel scrolling for a viewer.  Sub-classes may override as needed.  The
 * default implementation delegates to 
 * {@link #performViewerMouseWheel(Event, EditPartViewer)} IFF the tool is in the initial
 * state.  Mouse-wheel events generated at other times are ignored.
 * @param event the SWT scroll event
 * @param viewer the originating viewer
 * @see #performViewerMouseWheel(Event, EditPartViewer)
 */
public void mouseWheelScrolled(Event event, EditPartViewer viewer) {
	if (isInState(STATE_INITIAL))
		performViewerMouseWheel(event, viewer);
}

/**
 * Returns <code>true</code> if the threshold has been exceeded during a mouse drag.
 * @return <code>true</code> if the threshold has been exceeded
 */
protected boolean movedPastThreshold() {
	if (getFlag(FLAG_PAST_THRESHOLD))
		return true;
	Point start = getStartLocation(),
		  end = getLocation();
	if (Math.abs(start.x - end.x) > DRAG_THRESHOLD
	  || Math.abs(start.y - end.y) > DRAG_THRESHOLD) {
		setFlag(FLAG_PAST_THRESHOLD, true);
		return true;
	}
	return false;
}

/**
 * @see com.architexa.org.eclipse.gef.Tool#nativeDragFinished(DragSourceEvent, EditPartViewer)
 */
public void nativeDragFinished(DragSourceEvent event, EditPartViewer viewer) {
	if (!isViewerImportant(viewer))
		return;
	setViewer(viewer);
	handleNativeDragFinished(event);
}

/**
 * @see com.architexa.org.eclipse.gef.Tool#nativeDragStarted(DragSourceEvent, EditPartViewer)
 */
public void nativeDragStarted(DragSourceEvent event, EditPartViewer viewer) {
	if (!isViewerImportant(viewer))
		return;
	setViewer(viewer);
	handleNativeDragStarted(event);
}

/**
 * Delegates mouse-wheel event handling to registered 
 * {@link MouseWheelHandler MouseWheelHandlers} based on the given Event's statemask.  
 * Does nothing if there are no matching handlers found.
 * 
 * @param event the SWT scroll event
 * @param viewer the originating viewer
 * @since 3.1
 */
protected void performViewerMouseWheel(Event event, EditPartViewer viewer) {
	MouseWheelHandler handler = (MouseWheelHandler)viewer
			.getProperty(MouseWheelHandler.KeyGenerator.getKey(event.stateMask));
	if (handler != null)
		handler.handleMouseWheel(event, viewer);
}

void placeMouseInViewer(Point p) {
	if (getCurrentViewer() == null)
		return;
	Control c = getCurrentViewer().getControl();
	Rectangle rect;
	if (c instanceof Scrollable)
		rect = ((Scrollable)c).getClientArea();
	else
		rect = c.getBounds();
	if (p.x > rect.x + rect.width - 1)
		p.x = rect.x + rect.width - 1;
	else if (p.x < rect.x)
		p.x = rect.x;
	if (p.y > rect.y + rect.height - 1)
		p.y = rect.y + rect.height - 1;
	else if (p.y < rect.y)
		p.y = rect.y;
	org.eclipse.swt.graphics.Point swt = new org.eclipse.swt.graphics.Point(p.x, p.y);
	swt = c.toDisplay(swt);
	c.getDisplay().setCursorLocation(swt);
}

/**
 * Calls <code>deactivate()</code> and then <code>activate()</code>.
 */
protected void reactivate() {
	// Fix for Bug# 91448
	EditPartViewer viewer = getCurrentViewer();
	deactivate();
	activate();
	if (viewer != null) {
		Control c = viewer.getControl();
		if (c != null && !c.isDisposed() && c.isFocusControl())
			setViewer(viewer);
	}
}

/**
 * Sets the cursor being displayed to the appropriate cursor.  If the tool is active, the
 * current cursor being displayed is updated by calling {@link #calculateCursor()}.
 */
protected void refreshCursor() {
	if (isActive())
		setCursor(calculateCursor());
}

/**
 * Releases tool capture.
 * @see #setToolCapture()
 */
protected void releaseToolCapture() {
	getCurrentViewer().setRouteEventsToEditDomain(false);
}

/**
 * Convenience method to removes a figure from the feedback layer.
 * @param figure the figure being removed
 */
protected void removeFeedback(IFigure figure) {
	LayerManager lm = (LayerManager)getCurrentViewer().
		getEditPartRegistry().get(LayerManager.ID);
	if (lm == null)
		return;
	lm.getLayer(LayerConstants.FEEDBACK_LAYER).remove(figure);
}

/**
 * Resets all stateful flags to their initial values. Subclasses should extend this method
 * to reset their own custom flags.
 */
protected void resetFlags() {
	setFlag(FLAG_PAST_THRESHOLD, false);
	setFlag(FLAG_HOVER, false);
}

/**
 * Used to cache a command obtained from {@link #getCommand()}.
 * @param c the command
 * @see #getCurrentCommand()
 */
protected void setCurrentCommand(Command c) {
	command = c;
	refreshCursor();
}

/**
 * Shows the given cursor on the current viewer.
 * @param cursor the cursor to display
 */
protected void setCursor(Cursor cursor) {
	if (getCurrentViewer() != null)
		getCurrentViewer().setCursor(cursor);
}

/**
 * Sets the default cursor.
 * @param cursor the cursor
 * @see #getDefaultCursor()
 */
public void setDefaultCursor(Cursor cursor) {
	if (defaultCursor == cursor)
		return;
	defaultCursor = cursor;
	refreshCursor();
}

/**
 * Sets the disabled cursor.
 * @param cursor the cursor
 * @see #getDisabledCursor()
 */
public void setDisabledCursor(Cursor cursor) {
	if (disabledCursor == cursor)
		return;
	disabledCursor = cursor;
	refreshCursor();
}

/**
 * Sets the EditDomain.
 * @param domain the edit domain
 * @see #getDomain()
 */
public void setEditDomain(EditDomain domain) {
	this.domain = domain;
}

/**
 * Sets whether the hover flag is true or false.  Subclasses which do something on hover
 * can use this flag to track whether they have received a hover or not.
 * @param value whether hover is active
 */
protected void setHoverActive(boolean value) {
	setFlag(FLAG_HOVER, value);
}

void setMouseCapture(boolean value) {
	if (getCurrentViewer() != null
		&& getCurrentViewer().getControl() != null
		&& !getCurrentViewer().getControl().isDisposed())
		getCurrentViewer().getControl().setCapture(value);
}

/**
 * An example is {@link #PROPERTY_UNLOAD_WHEN_FINISHED} -> Boolean.  AbstractTool uses
 * introspection to set properties that are not explicitly specified.  For instance,
 * the key "defaultCursor" will cause {@link #setDefaultCursor(Cursor)} to be invoked
 * with the given value. 
 * @see com.architexa.org.eclipse.gef.Tool#setProperties(java.util.Map)
 */
public void setProperties(Map properties) {
	if (properties == null)
		return;
	Iterator entries = properties.entrySet().iterator();
	while (entries.hasNext()) {
		Entry entry = (Entry)entries.next();
		applyProperty(entry.getKey(), entry.getValue());
	}
}

/**
 * Sets the start mouse location, typically for a drag operation.
 * @param p the start location
 */
protected void setStartLocation(Point p) {
	startX = p.x;
	startY = p.y;
}

/**
 * Sets the tools state.
 * @param state the new state
 */
protected void setState(int state) {
	this.state = state;
}

/**
 * Sets tool capture.  When a tool has capture, viewers will make every effort to send
 * events through the editdomain to the tool.  Therefore, the default handling of some
 * events is bypassed.
 */
protected void setToolCapture() {
	getCurrentViewer().setRouteEventsToEditDomain(true);
}

/**
 * Setting this to <code>true</code> will cause the tool to be unloaded after one
 * operation has completed.  The default value is <code>true</code>.  The tool is
 * unloaded, and the edit domains default tool will be activated.
 * @param value whether the tool should be unloaded on completion
 */
public void setUnloadWhenFinished(boolean value) {
	setFlag(FLAG_UNLOAD, value);
}

/**
 * Sets the active EditPartViewer.  The active viewer is the viewer from which the last
 * event was received.
 * @param viewer the viewer
 */
public void setViewer(EditPartViewer viewer) {
	if (viewer == currentViewer)
		return;

	setCursor(null);
	currentViewer = viewer;
	if (currentViewer != null) {
		org.eclipse.swt.graphics.Point p = currentViewer.getControl().toControl(
			Display.getCurrent().getCursorLocation());
		getCurrentInput().setMouseLocation(p.x, p.y);
	}
	refreshCursor();
}

/**
 * Returns <code>true</code> if the give state transition succeeds. This is a "test and
 * set" operation, where the tool is tested to be in the specified start state, and if so,
 * is set to the given end state.  The method returns the result of the first test.
 * @param start the start state being tested
 * @param end the end state
 * @return <code>true</code> if the state transition is successful
 */
protected boolean stateTransition(int start, int end) {
	if ((getState() & start) != 0) {
		setState(end);
		return true;
	} else
		return false;
}

/**
 * Returns <code>true</code> if the tool is set to unload when its current operation is
 * complete.
 * @return <code>true</code> if the tool should be unloaded when finished
 */
protected final boolean unloadWhenFinished() {
	return getFlag(FLAG_UNLOAD);
}

/**
 * Receives the mouse entered event.  Subclasses wanting to handle this event should 
 * override {@link #handleViewerEntered()}.
 * <p>
 * FEATURE in SWT: mouseExit comes after mouseEntered on the new control. Therefore, if 
 * the current viewer is not <code>null</code>, it means the exit has not been sent yet by
 * SWT. To maintain proper ordering, GEF fakes the exit and calls {@link
 * #handleViewerExited()}. The real exit will then be ignored.
 * @param me the mouse event
 * @param viewer the originating viewer
 */
public void viewerEntered(MouseEvent me, EditPartViewer viewer) {
	if (!isViewerImportant(viewer))
		return;
	getCurrentInput().setInput(me);
	if (getCurrentViewer() != null && getCurrentViewer() != viewer)
		handleViewerExited();
	setViewer(viewer);
	handleViewerEntered();
}

/**
 * Handles the mouse exited event.  Subclasses wanting to handle this event should
 * override {@link #handleViewerExited()}.
 * @param me the mouse event
 * @param viewer the originating viewer
 */
public void viewerExited(MouseEvent me, EditPartViewer viewer) {
	/*
	 * FEATURE in SWT. mouseExited comes after mouseEntered. So only call handle exit if
	 * we didn't previously fake it on viewer entered.
	 */
	if (viewer == getCurrentViewer()) {
		getCurrentInput().setInput(me);
		handleViewerExited();
		setViewer(null);
	}
}

/**
 * Allows the user to access mouse and keyboard input.
 */
public static class Input
	extends com.architexa.org.eclipse.gef.util.FlagSupport
{
	int modifiers;
	Point mouse = new Point();
	boolean verifyMouseButtons;

	/**
	 * Returns the event modifiers. Modifiers are defined in {@link MouseEvent#stateMask},
	 * and include things like the mouse buttons and keyboard modifier keys.
	 * @return the event modifiers
	 */
	protected int getModifiers() {
		return modifiers;
	}
	
	/**
	 * Returns the current location of the mouse.
	 * @return the mouse location
	 */
	public Point getMouseLocation() {
		return mouse;
	}

	/**
	 * Returns <code>true</code> if the ALT key is pressed.
	 * @return <code>true</code> if the ALT key is pressed
	 */
	public boolean isAltKeyDown() {
		return (modifiers & SWT.ALT) != 0;
	}
	
	/**
	 * Returns <code>true</code> if any of the mouse buttons are pressed.
	 * @return <code>true</code> if any of the mouse buttons are pressed
	 */
	public boolean isAnyButtonDown() {
		return getFlag(2 | 4 | 8 | 16 | 32);
	}
	
	/**
	 * Returns <code>true</code> if the CTRL key is pressed.
	 * @return <code>true</code> of CTRL pressed
	 */
	public boolean isControlKeyDown() {
		return (modifiers & SWT.CONTROL) != 0;
	}
	
	/**
	 * Returns <code>true</code> if any of the given mod keys are pressed.
	 * @param mod SWT.MOD1, SWT.MOD2, SWT.MOD3, SWT.MOD4 or any combination thereof
	 * @return <code>true</code> if the given mod key is pressed
	 * @since 3.1
	 */
	public boolean isModKeyDown(int mod) {
		return (modifiers & mod) != 0;
	}
	
	/**
	 * Returns <code>true</code> if the specified button is down.
	 * @param which which button
	 * @return <code>true</code> if the button is down
	 */
	public boolean isMouseButtonDown(int which) {
		return getFlag(1 << which);
	}
	
	/**
	 * Returns <code>true</code> if the SHIFT key is pressed.
	 * @return <code>true</code> if SHIFT pressed
	 */
	public boolean isShiftKeyDown() {
		return (modifiers & SWT.SHIFT) != 0;
	}
	
	/**
	 * Sets the keyboard input based on the KeyEvent.
	 * @param ke the key event providing the input
	 */
	public void setInput(KeyEvent ke) {
		modifiers = ke.stateMask;
	}

	/**
	 * Sets the mouse and keyboard input based on the MouseEvent.
	 * @param me the mouse event providing the input
	 */
	public void setInput(MouseEvent me) {
		setMouseLocation(me.x, me.y);
		modifiers = me.stateMask;
		if (verifyMouseButtons) {
			setMouseButton(1, (modifiers & SWT.BUTTON1) != 0);
			setMouseButton(2, (modifiers & SWT.BUTTON2) != 0);
			setMouseButton(3, (modifiers & SWT.BUTTON3) != 0);
			setMouseButton(4, (modifiers & SWT.BUTTON4) != 0);
			setMouseButton(5, (modifiers & SWT.BUTTON5) != 0);
			verifyMouseButtons = false;
		}
	}
	
	/**
	 * Sets mouse button # <code>which</code> to be pressed if <code>state</code> is true.
	 * @param which which button
	 * @param state <code>true</code> if button down
	 */
	public void setMouseButton(int which, boolean state) {
		setFlag(1 << which, state);
	}
	
	void setMouseLocation(int x, int y) {
		mouse.setLocation(x, y);
	}
}

}
